package ig.finanzas;

import ig.util.Regresion;
import ig.util.excepciones.DatoInvalidoExcepcion;
import ig.util.excepciones.IGExcepcion;
import ig.util.excepciones.LogicaExcepcion;
import java.io.Serializable;
//import java.sql.Timestamp;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.TreeSet;
// import oracle.sql.TIMESTAMP;
/**
 * Esta clase modela una coleccion de valores historicos y algunas funciones basicas requeridas sobre la misma. Se especifica un valor para cada una de las fechas registradas. En algunos casos, cada fecha se asocia con un unico valor, pero existen casos en que es  necesario asignar a una fecha un conjunto de valores (Tasa Curva Cero Cupon). El atributo  numValoresXRegistro define la cantidad de valores que se deben almacenar por cada registro.
 * @author  lfgarcia
 */
public abstract class ManejadorValoresHistoricos implements Serializable {

    public ManejadorValoresHistoricos(int numValoresXRegistro) throws DatoInvalidoExcepcion {
        if (numValoresXRegistro <= 0) {
            throw new DatoInvalidoExcepcion("Numero de valores por registro menor o igual a cero.");
        }

        this.numValoresXRegistro = numValoresXRegistro;
        valoresHistoricos       = new HashMap<java.sql.Timestamp, double[]>();
        //Modificado por Cristhiam 
        valoresHistoricos2rr    = new HashMap<Timestamp, double[][]>();
    }

    /**
     * Adiciona un valor historico a la coleccion.
     * @param fecha
     * @param valor
     * @throws DatoInvalidoExcepcion Si la fecha es nula o 
     * por registro en la fecha especificada no coinciden con los esperados.
     */
    public void adicionarValor(Date fecha, double[] valor) throws DatoInvalidoExcepcion {
        if (fecha == null) {
            throw new DatoInvalidoExcepcion("La fecha del valor historico a adicionar es nula.");
        }

        if (valor.length != numValoresXRegistro) {
            throw new DatoInvalidoExcepcion("El numero de valores por registro del valor historico no concuerda con el esperado.");
        }
//        System.out.println("puc:"+valor);
        if (valor == null) {
            System.out.println(fecha + " = puc:" + valor);
        }
        valoresHistoricos.put(this.getKey(fecha), valor);
    }

    /**
     * Devuelve el conjunto de valores registrados a una fecha especifica.
     * @param fecha
     * @return double[] con el conjunto de valores.
     * @throws DatoInvalidoExcepcion Si la fecha es nula.
     * @throws LogicaExcepcion Si no hay suficiente informacion para determinar el valor vigente.
     */
    public double[] valorVigenteA(Date fecha) throws DatoInvalidoExcepcion, LogicaExcepcion {
        if (fecha == null) {
            throw new DatoInvalidoExcepcion("La fecha del valor a hallar es nula.");
        }

        if (valoresHistoricos.size() <= 0) {
            throw new LogicaExcepcion("No hay suficiente informacion registrada para hallar un valor historico.");
        }



        //Si hay valor para la fecha especificada se devuelve dicho valor
        if (valoresHistoricos.containsKey(this.getKey(fecha))) {
//            Object valor = valoresHistoricos.get(fecha);
//            System.out.println("If 2:"+valor);
            return (double[]) valoresHistoricos.get(this.getKey(fecha));
        }

        GregorianCalendar gc = new GregorianCalendar();
        gc.setTimeInMillis(this.getKey(fecha).getTime());
        //TODO seria conveniente agregar este codigo en todas partes por si utilizan el constructor de fechas simple
        gc.add(Calendar.HOUR, gc.get(Calendar.HOUR) * -1);
        gc.add(Calendar.MINUTE, gc.get(Calendar.MINUTE) * -1);
        gc.add(Calendar.SECOND, gc.get(Calendar.SECOND) * -1);
        gc.add(Calendar.MILLISECOND, gc.get(Calendar.MILLISECOND) * -1);
//        if(gc.get(gc.YEAR)!=fecha.getYear())
//            gc.add(gc.YEAR,-1900);
        //Obtengo la ultima fecha registrada
        TreeSet fechasregistradas = new TreeSet(valoresHistoricos.keySet());
        java.sql.Timestamp fechaMasAntiguaRegistrada = (java.sql.Timestamp) fechasregistradas.first();


        //Si hay valores pero no tan viejos entonces error
        if (fechaMasAntiguaRegistrada.getTime() > this.getKey(gc.getTime()).getTime()) {
            throw new LogicaExcepcion("No hay suficiente informacion registrada para hallar un valor historico.");
        }

        //Se resta un dia a la fecha hasta que se encuentre valor para ella
        Date fechaAnteriorMasReciente = null;
        while (fechaAnteriorMasReciente == null) {
            gc.add(Calendar.DATE, -1);
            if (valoresHistoricos.containsKey(this.getKey(gc.getTime()))) {
//                System.out.println("If 3 en el while");
                return (double[]) valoresHistoricos.get(this.getKey(gc.getTime()));
            }
        }

        //Esta parte nunca se ejecutara
        return null;
    }

    /**
     * Devuelve la fecha anterior a la especificada para la que existe valor registrado.
     * @param fecha
     * @return
     * @throws DatoInvalidoExcepcion Si la fecha es nula.
     * @throws LogicaExcepcion Si no hay suficiente informacion para determinar la fecha anterior.
     */
    public Date fechaAntesDe(Date fecha) throws DatoInvalidoExcepcion, LogicaExcepcion {
        if (fecha == null) {
            throw new DatoInvalidoExcepcion("La fecha especificada es nula.");
        }

        if (valoresHistoricos.size() <= 0) {
            throw new LogicaExcepcion("No hay suficiente informacion registrada para determinar una fecha.");
        }

        GregorianCalendar gc = new GregorianCalendar();
        gc.setTimeInMillis(this.getKey(fecha).getTime());

        //TODO ojo aqui se utilizo otra forma de organizacion de las fechas
        ArrayList fechas = new ArrayList(valoresHistoricos.keySet());
        Collections.sort(fechas);

        java.sql.Timestamp fechaMasAntiguaRegistrada = (java.sql.Timestamp) fechas.get(0);
        if (fechaMasAntiguaRegistrada.getTime() >= this.getKey(fecha).getTime()) {
            throw new LogicaExcepcion("No hay suficiente informacion registrada para determinar una fecha.");
        }

        Date fechaMasReciente = null;
        while (fechaMasReciente == null) {
            gc.add(Calendar.DATE, -1);
            if (valoresHistoricos.containsKey(this.getKey(gc.getTime()))) {
                return gc.getTime();
            }
        }

        return null;
    }

    /**
     * Computa los valores registrados en una fecha con el objetivo de tener un unico valor.
     * En caso de trabajar con una curva cero cupon aplica la formula sobre los betas para obtener
     * un unico valor. En general es mas conveniente utilizar esta funcion que el de valorVigenteA.
     * @param fecha
     * @param aux1 En caso de una curva cero cupon especifica el numero de dias para calcular el valor.
     * @return
     * @throws DatoInvalidoExcepcion
     * @throws LogicaExcepcion
     */
    public abstract double obtenerValorVigenteA(Date fecha, int aux1) throws DatoInvalidoExcepcion, LogicaExcepcion;

    /*
     * Lineas Creadas Por Cristhiam Reina
     */
    public abstract double obtenerValorVigentePlazoA(Date fecha, int aux1) throws DatoInvalidoExcepcion, LogicaExcepcion;
    
    /**
     * Devuelve el cambio esperado en la serie de valores calculado a partir de la forma en que lo
     * especifique el DefinidorCalculoCambioEsperado.
     * @param fecha
     * @param definidor
     * @return
     * @throws DatoInvalidoExcepcion Si alguno de los parametros recibidos es invalido.
     * @throws LogicaExcepcion Si la forma de calculo no permite obtener el valor, si no hay suficientes
     * valores en la serie, si la informacion hallada no alcanza a ser una muestra estadisticamente representativa,
     * o si la serie no permite el calculo utilizando diferencias logaritmicas.
     */
    public double cambioEsperado(Date fecha, DefinidorCalculoCambioEsperado definidor, Boolean isPrecio)
            throws DatoInvalidoExcepcion, LogicaExcepcion {

        if (fecha == null) {
            throw new DatoInvalidoExcepcion("La fecha especificada es nula.");
        }

        if (definidor == null) {
            throw new DatoInvalidoExcepcion("No se ha definido una forma para calcular el cambio esperado.");
        }

        if (definidor.getFormaCalculoCambioEsperado() == DefinidorCalculoCambioEsperado.FORMA_CALCULOCAMBIOESPERADO_VALORESPECIFICADO) {
            return definidor.getValor();
        }

        if (valoresHistoricos.size() == 0) {
            throw new LogicaExcepcion("La serie de valores no tiene suficientes valores registrados para efectuar el analisis.");
        }


        if (definidor.getFormaCalculoCambioEsperado() == DefinidorCalculoCambioEsperado.FORMA_CALCULOCAMBIOESPERADO_NORMAL) {
            GregorianCalendar gc = new GregorianCalendar();

            gc.setTimeInMillis(this.getKey(fecha).getTime());
            ArrayList observaciones = new ArrayList();
            for (int i = 0; i < definidor.getNumobservaciones(); i++) {

                double pt;
                try {
                    pt = obtenerValorVigenteA(gc.getTime(), definidor.getNumdias());
                } catch (LogicaExcepcion e) {
                    //Si no se pueden obtener mas valores
                    break;
                }
                observaciones.add(new Double(pt));
                try {
                    gc.setTimeInMillis(this.getKey(fechaAntesDe(gc.getTime())).getTime());
                } catch (IGExcepcion e) {
                    //Si la fecha es nula nunca ocurre
                    //Si no hay mas fechas anteriores ya no voy a meter mas en las observaciones
                    break;
                }

            }

            if (observaciones.size() <= 3) {
            //throw new LogicaExcepcion("La informacion hallada no alcanza a ser una muestra estadisticamente representativa.");
            }

            double cambio = 0;
            try {
                double numeroDesviacionesNormales = LibreriaOperacionesEstadisticas.numeroDesviacionesNormales(definidor.getCoeficienteconfianza());
                cambio = LibreriaOperacionesEstadisticas.desviacion(observaciones) * numeroDesviacionesNormales;
            } catch (DatoInvalidoExcepcion e) {
            //Esto nunca va a suceder, por que la coleccion siempre tendra valores
            }
            return cambio;

        } else if (definidor.getFormaCalculoCambioEsperado() == DefinidorCalculoCambioEsperado.FORMA_CALCULOCAMBIOESPERADO_EWMA) {

            GregorianCalendar gc = new GregorianCalendar();

            gc.setTimeInMillis(this.getKey(fecha).getTime());
            double precioVigente = obtenerValorVigenteA(gc.getTime(), definidor.getNumdias());
            ArrayList observaciones = new ArrayList();
            for (int i = 0; i < definidor.getNumobservaciones(); i++) {
                GregorianCalendar gc2 = new GregorianCalendar();

                gc2.setTimeInMillis(this.getKey(gc.getTime()).getTime());
                gc2.add(Calendar.DATE, definidor.getLongperiodo()* -1);
                double diferencia;
                double hoy;
                double antes;
                try {
                    hoy = obtenerValorVigenteA(gc.getTime(), definidor.getNumdias());
                    antes = obtenerValorVigenteA(gc2.getTime(), definidor.getNumdias());
                } catch (LogicaExcepcion e) {
                    //Si no hay suficiente informacion registrada
                    break;
                }

               if ((hoy < 0) || (antes <= 0)) {
                   throw new LogicaExcepcion("No se puede efectuar el calculo con diferencias logaritmicas");
               }
               
                /*
                 * Modificado por Cristhiam Reina
                 * 1 de septiembre del 2011
                 * agregar operaciones de LOGARITMICAS, ARITMETICA Y GEOMETRICA.
                 */

                
                /*
                 * Modificado por Eliecer Cortes
                 * 1 de septiembre del 2011
                 * agregar operaciones de NO CALCULAR.
                 */
                
                if (definidor.getTipodiferencia() == DefinidorCalculoCambioEsperado.NO_CALCULAR_DIF) {
                    diferencia = hoy;
                 // Fin Modificacion Eliecer Cortes   
                }else if (definidor.getTipodiferencia() == DefinidorCalculoCambioEsperado.DIF_LOGARITMICA) {
                    if ((hoy < 0) || (antes <= 0)) {
                        throw new LogicaExcepcion("No se puede efectuar el calculo con diferencias logaritmicas");
                    }
                    diferencia = Math.log(hoy / antes);
                }else if (definidor.getTipodiferencia() == DefinidorCalculoCambioEsperado.DIF_ARITMETICA){
                    //diferencia = obtenerValorVigenteA(gc.getTime(), numdias) - obtenerValorVigenteA(gc2.getTime(), numdias);
                    diferencia = hoy - antes;
                } else{
                    //diferencia = (hoy - antes) / antes;
                    diferencia = (hoy /antes) - 1;
                }

                System.out.println("Valor actual: " + hoy);
                System.out.println("Valor anterior: " + antes);
                System.out.println("Diferencia: " + diferencia);
                //System.out.println("Diferencia: "+diferencia);
                observaciones.add(new Double(diferencia));
                try {
                    gc.setTimeInMillis(this.getKey(fechaAntesDe(gc.getTime())).getTime());
                } catch (LogicaExcepcion e1) {
                    //Si no hay suficiente informacion registrada
                    break;
                }

//                double pt;
//                try {
//                    pt = obtenerValorVigenteA(gc.getTime(), definidor.getNumdias());
//                } catch (LogicaExcepcion e) {
//                    //Si no se pueden obtener mas valores
//                    break;
//                }
//                observaciones.add(new Double(pt));
//                try {
//                    gc.setTimeInMillis(this.getKey(fechaAntesDe(gc.getTime())).getTime());
//                } catch (IGExcepcion e) {
//                    //Si la fecha es nula nunca ocurre
//                    //Si no hay mas fechas anteriores ya no voy a meter mas en las observaciones
//                    break;
//                }

            }

            if (observaciones.size() <= 3) {
            //throw new LogicaExcepcion("La informacion hallada no alcanza a ser una muestra estadisticamente representativa.");
            }


            double cambio = 0;
            try {
                cambio = LibreriaOperacionesEstadisticas.ewma(observaciones, definidor.getLambda()) * LibreriaOperacionesEstadisticas.numeroDesviacionesNormales(definidor.getCoeficienteconfianza());
            } catch (DatoInvalidoExcepcion e) {
            //Esto nunca va a suceder, por que la coleccion siempre tendra valores
            }
            if (!isPrecio) {
                System.out.println("Cambio: " + cambio);
                System.out.println("Precio vigente: " + precioVigente);
                cambio = cambio * precioVigente;
            }

            return cambio;

           /* GregorianCalendar gc = new GregorianCalendar();

            gc.setTimeInMillis(this.getKey(fecha).getTime());
            ArrayList observaciones = new ArrayList();
            for (int i = 0; i < definidor.getNumobservaciones(); i++) {

                double pt;
                try {
                    pt = obtenerValorVigenteA(gc.getTime(), definidor.getNumdias());
                } catch (LogicaExcepcion e) {
                    //Si no se pueden obtener mas valores
                    break;
                }
                observaciones.add(new Double(pt));
                try {
                    gc.setTimeInMillis(this.getKey(fechaAntesDe(gc.getTime())).getTime());
                } catch (IGExcepcion e) {
                    //Si la fecha es nula nunca ocurre
                    //Si no hay mas fechas anteriores ya no voy a meter mas en las observaciones
                    break;
                }

            }

            if (observaciones.size() <= 3) {
            //throw new LogicaExcepcion("La informacion hallada no alcanza a ser una muestra estadisticamente representativa.");
            }


            double cambio = 0;
            try {
                cambio = LibreriaOperacionesEstadisticas.ewma(observaciones, definidor.getLambda()) * LibreriaOperacionesEstadisticas.numeroDesviacionesNormales(definidor.getCoeficienteconfianza());
            } catch (DatoInvalidoExcepcion e) {
            //Esto nunca va a suceder, por que la coleccion siempre tendra valores
            }
            return cambio;*/

        } else if (definidor.getFormaCalculoCambioEsperado() == DefinidorCalculoCambioEsperado.FORMA_VALORACION_HISTORICA){
            double resultado = 0;
            ArrayList observaciones = new ArrayList();

            GregorianCalendar gc = new GregorianCalendar();
            gc.setTimeInMillis(this.getKey(fecha).getTime());
            
            for (int i = 0; i < definidor.getNumobservaciones(); i++) {
                GregorianCalendar gc2 = new GregorianCalendar();
                gc2.setTimeInMillis(this.getKey(gc.getTime()).getTime());
                gc2.add(Calendar.DATE, definidor.getLongperiodo() * -1);
                double diferencia;
                double hoy;
                double antes;
                
                try {
                    hoy = obtenerValorVigenteA(gc.getTime(), definidor.getNumdias());
                    antes = obtenerValorVigenteA(gc2.getTime(), definidor.getNumdias());
                } catch (LogicaExcepcion e) {
                    //Si no hay suficiente informacion registrada
                    break;
                }

                /*
                 * Modificado por Eliecer Cortes
                 * 1 de septiembre del 2011
                 * agregar operaciones de NO CALCULAR.
                 */
                
                if (definidor.getTipodiferencia() == DefinidorCalculoCambioEsperado.NO_CALCULAR_DIF) {
                    diferencia = hoy;
                 // Fin Modificacion Eliecer Cortes   
                }else if (definidor.getTipodiferencia() == DefinidorCalculoCambioEsperado.DIF_LOGARITMICA) {
                    if ((hoy < 0) || (antes <= 0)) {
                        throw new LogicaExcepcion("No se puede efectuar el calculo con diferencias logaritmicas");
                    }
                    diferencia = Math.log(hoy / antes);
                }
                /*
                 * Modificado por Cristhiam Reina
                 * 5 de marzo del 2011
                 * modificacion del if
                 */
                else if (definidor.getTipodiferencia() == DefinidorCalculoCambioEsperado.DIF_ARITMETICA){
                    //diferencia = obtenerValorVigenteA(gc.getTime(), numdias) - obtenerValorVigenteA(gc2.getTime(), numdias);
                    diferencia = hoy - antes;
                } else{
                    //diferencia = (hoy - antes) / antes;
                    diferencia = (hoy /antes) - 1;
                }


                //System.out.println("Diferencia: "+diferencia);
                observaciones.add(new Double(diferencia));
                try {
                    gc.setTimeInMillis(this.getKey(fechaAntesDe(gc.getTime())).getTime());
                } catch (LogicaExcepcion e1) {
                    //Si no hay suficiente informacion registrada
                    break;
                }
            }


            boolean logaritmica = false;
            if (definidor.getTipodiferencia() == DefinidorCalculoCambioEsperado.DIF_LOGARITMICA) {
                logaritmica = true;
            }

            resultado = LibreriaOperacionesEstadisticas.percentil(observaciones, definidor.getCoeficienteconfianza(), logaritmica, definidor.getLongperiodo());
                                                        
            return resultado;
        }else if (definidor.getFormaCalculoCambioEsperado() == DefinidorCalculoCambioEsperado.FORMA_REGRESION_LINIAL){
            double resultado = 0;

            ArrayList observaciones = new ArrayList();
            ArrayList fechas = new ArrayList();
            

            GregorianCalendar gc = new GregorianCalendar();
            gc.setTimeInMillis(this.getKey(fecha).getTime());

            for (int i = 0; i < definidor.getNumobservaciones(); i++) {
                GregorianCalendar gc2 = new GregorianCalendar();
                gc2.setTimeInMillis(this.getKey(gc.getTime()).getTime());
                gc2.add(Calendar.DATE, definidor.getLongperiodo() * -1);

                double diferencia;
                double hoy;
                double antes;
                try {
                    hoy = obtenerValorVigenteA(gc.getTime(), definidor.getNumdias());
                    antes = obtenerValorVigenteA(gc2.getTime(), definidor.getNumdias());
                } catch (LogicaExcepcion e) {
                    //Si no hay suficiente informacion registrada
                    break;
                }

                /*
                 * Modificado por Eliecer Cortes
                 * 1 de septiembre del 2011
                 * agregar operaciones de NO CALCULAR.
                 */

                if (definidor.getTipodiferencia() == DefinidorCalculoCambioEsperado.NO_CALCULAR_DIF) {
                    diferencia = hoy;
                 // Fin Modificacion Eliecer Cortes
                }else if (definidor.getTipodiferencia() == DefinidorCalculoCambioEsperado.DIF_LOGARITMICA) {
                    if ((hoy < 0) || (antes <= 0)) {
                        throw new LogicaExcepcion("No se puede efectuar el calculo con diferencias logaritmicas");
                    }
                    diferencia = Math.log(hoy / antes);
                }
                /*
                 * Modificado por Cristhiam Reina
                 * 5 de marzo del 2011
                 * modificacion del if
                 */
                else if (definidor.getTipodiferencia() == DefinidorCalculoCambioEsperado.DIF_ARITMETICA){
                    //diferencia = obtenerValorVigenteA(gc.getTime(), numdias) - obtenerValorVigenteA(gc2.getTime(), numdias);
                    diferencia = hoy - antes;
                } else{
                    //diferencia = (hoy - antes) / antes;
                    diferencia = (hoy /antes) - 1;
                }

                //System.out.println("Diferencia: "+diferencia);
                observaciones.add(new Double(diferencia));
                fechas.add((gc2.getTimeInMillis() * 1.1574074074074E-8) + 25933);

                try {
                    gc.setTimeInMillis(this.getKey(fechaAntesDe(gc.getTime())).getTime());

                } catch (LogicaExcepcion e1) {
                    //Si no hay suficiente informacion registrada
                    break;
                }
            }



            boolean logaritmica = false;
            if (definidor.getTipodiferencia() == DefinidorCalculoCambioEsperado.DIF_LOGARITMICA) {
                logaritmica = true;
            }

            Regresion regresion  = new Regresion(fechas, fechas);
            resultado = LibreriaOperacionesEstadisticas.regresionLineal(regresion, definidor.getLongperiodo());

            return resultado;
        } else {
            GregorianCalendar gc = new GregorianCalendar();
            gc.setTimeInMillis(this.getKey(fecha).getTime());
            double precioVigente = obtenerValorVigenteA(gc.getTime(), definidor.getNumdias());
            ArrayList observaciones = new ArrayList();
            for (int i = 0; i < definidor.getNumobservaciones(); i++) {
                GregorianCalendar gc2 = new GregorianCalendar();
                gc2.setTimeInMillis(this.getKey(gc.getTime()).getTime());
                gc2.add(Calendar.DATE, definidor.getLongperiodo() * -1);
                double diferencia;
                double hoy;
                double antes;
                try {
                    hoy = obtenerValorVigenteA(gc.getTime(), definidor.getNumdias());
                    antes = obtenerValorVigenteA(gc2.getTime(), definidor.getNumdias());
                } catch (LogicaExcepcion e) {
                    //Si no hay suficiente informacion registrada
                    break;
                }
                /*
                 * Modificado por Eliecer Cortes
                 * 1 de septiembre del 2011
                 * agregar operaciones de NO CALCULAR.
                 */

                if (definidor.getTipodiferencia() == DefinidorCalculoCambioEsperado.NO_CALCULAR_DIF) {
                    diferencia = hoy;
                 // Fin Modificacion Eliecer Cortes
                }else if (definidor.getTipodiferencia() == DefinidorCalculoCambioEsperado.DIF_LOGARITMICA) {
                    if ((hoy < 0) || (antes <= 0)) {
                        throw new LogicaExcepcion("No se puede efectuar el calculo con diferencias logaritmicas");
                    }
                    diferencia = Math.log(hoy / antes);
                } else if(definidor.getTipodiferencia() == DefinidorCalculoCambioEsperado.DIF_ARITMETICA) {
                    //diferencia = obtenerValorVigenteA(gc.getTime(), numdias) - obtenerValorVigenteA(gc2.getTime(), numdias);
                    diferencia = hoy - antes;
                }else{
                    diferencia = (hoy/antes) - 1;
                }

                //System.out.println("Diferencia: "+diferencia);
                observaciones.add(new Double(diferencia));
                try {
                    gc.setTimeInMillis(this.getKey(fechaAntesDe(gc.getTime())).getTime());
                } catch (LogicaExcepcion e1) {
                    //Si no hay suficiente informacion registrada
                    break;
                }
            }

            //if ( observaciones.size() <3 )
            //	throw new LogicaExcepcion("La informacion hallada no alcanza a ser una muestra estadisticamente representativa.");

            boolean logaritmica = false;
            if (definidor.getTipodiferencia() == DefinidorCalculoCambioEsperado.DIF_LOGARITMICA) {
                logaritmica = true;
            }

            
            double resultado = 0;
            try {
                if(definidor.getFormaCalculoCambioEsperado() == DefinidorCalculoCambioEsperado.FORMA_VALORACION_HISTORICA){
                    resultado = LibreriaOperacionesEstadisticas.percentil(observaciones, precioVigente, logaritmica, numValoresXRegistro);
                }
                else if (definidor.getFormaCalculoCambioEsperado() == DefinidorCalculoCambioEsperado.FORMA_CALCULOCAMBIOESPERADO_ELIECER_NO_SUMAR_PROMEDIO) {
                    resultado = LibreriaOperacionesEstadisticas.eliecer(observaciones, logaritmica, definidor.getCoeficienteconfianza(), false);
                } else {
                    resultado = LibreriaOperacionesEstadisticas.eliecer(observaciones, logaritmica, definidor.getCoeficienteconfianza(), true);
                }
            } catch (DatoInvalidoExcepcion e) {
            //Esto no sucede, por que la coleccion si tiene valores registrados.
            }

            try {
                if (definidor.getFormaCalculoCambioEsperado() == DefinidorCalculoCambioEsperado.FORMA_CALCULOCAMBIOESPERADO_ELIECER_NO_SUMAR_PROMEDIO_MAX_OBSERVACION) {
                    resultado = LibreriaOperacionesEstadisticas.observacionMaxima(observaciones);
                }
            } catch (DatoInvalidoExcepcion e) {
            //Esto no sucede, por que la coleccion si tiene valores registrados.
            }

            /*Para pruebas de Eliecer*/
            System.out.println("Precio Vigente es: " + precioVigente);

            // Corregido ECS 26/09/2011  if (logaritmica && !isPrecio) {
            if (!isPrecio) {
                resultado = resultado * precioVigente;
            }
            return resultado;
        }
    }

    private java.sql.Timestamp getKey(Date fecha) {
        java.sql.Date date = new java.sql.Date(fecha.getTime());

        GregorianCalendar gc = new GregorianCalendar();
        gc.setTimeInMillis(date.getTime());

        return new java.sql.Timestamp(gc.get(GregorianCalendar.YEAR) - 1900, gc.get(GregorianCalendar.MONTH), gc.get(GregorianCalendar.DATE), 0, 0, 0, 0);
    }
//    public static void main(String[] arg){
//        GregorianCalendar gc = new GregorianCalendar();
//        gc.setTimeInMillis(new java.sql.Date(new java.util.Date("2007/05/14").getTime()).getTime());
//        
//        
//        System.out.println(gc.getTime());
//        System.out.println(new java.sql.Timestamp(gc.get(gc.YEAR)-1900,gc.get(gc.MONTH),gc.get(gc.DATE),0,0,0,0));
//        
//    }
    private int numValoresXRegistro;
    private HashMap<java.sql.Timestamp, double[]> valoresHistoricos;

    /*
     * Creado Por Cristhiam Reina
     * Creado El 18/07/2011
     */
    public double[][] obtenerValorVigentePlazoA(Date fecha) throws DatoInvalidoExcepcion, LogicaExcepcion {
        if (fecha == null) {
            throw new DatoInvalidoExcepcion("La fecha del valor a hallar es nula.");
        }

        if (valoresHistoricos2rr.size() <= 0) {
            throw new LogicaExcepcion("No hay suficiente informacion registrada para hallar un valor historico.");
        }



        //Si hay valor para la fecha especificada se devuelve dicho valor
        if (valoresHistoricos2rr.containsKey(this.getKey(fecha))) {
//            Object valor = valoresHistoricos.get(fecha);
//            System.out.println("If 2:"+valor);
            return (double[][]) valoresHistoricos2rr.get(this.getKey(fecha));
        }

        GregorianCalendar gc = new GregorianCalendar();
        gc.setTimeInMillis(this.getKey(fecha).getTime());
        //TODO seria conveniente agregar este codigo en todas partes por si utilizan el constructor de fechas simple
        gc.add(Calendar.HOUR, gc.get(Calendar.HOUR) * -1);
        gc.add(Calendar.MINUTE, gc.get(Calendar.MINUTE) * -1);
        gc.add(Calendar.SECOND, gc.get(Calendar.SECOND) * -1);
        gc.add(Calendar.MILLISECOND, gc.get(Calendar.MILLISECOND) * -1);
//        if(gc.get(gc.YEAR)!=fecha.getYear())
//            gc.add(gc.YEAR,-1900);
        //Obtengo la ultima fecha registrada
        TreeSet fechasregistradas = new TreeSet(valoresHistoricos2rr.keySet());
        java.sql.Timestamp fechaMasAntiguaRegistrada = (java.sql.Timestamp) fechasregistradas.first();


        //Si hay valores pero no tan viejos entonces error
        if (fechaMasAntiguaRegistrada.getTime() > this.getKey(gc.getTime()).getTime()) {
            throw new LogicaExcepcion("No hay suficiente informacion registrada para hallar un valor historico.");
        }

        //Se resta un dia a la fecha hasta que se encuentre valor para ella
        Date fechaAnteriorMasReciente = null;
        while (fechaAnteriorMasReciente == null) {
            gc.add(Calendar.DATE, -1);
            if (valoresHistoricos2rr.containsKey(this.getKey(gc.getTime()))) {
//                System.out.println("If 3 en el while");
                return (double[][]) valoresHistoricos2rr.get(this.getKey(gc.getTime()));
            }
        }

        //Esta parte nunca se ejecutara
        return null;
    }


    public void adicionarValorArr2(Date fecha, double[][] valor) throws DatoInvalidoExcepcion {
        if (fecha == null) {
            throw new DatoInvalidoExcepcion("La fecha del valor historico a adicionar es nula.");
        }

        if (valor.length != numValoresXRegistro) {
            throw new DatoInvalidoExcepcion("El numero de valores por registro del valor historico no concuerda con el esperado.");
        }
//        System.out.println("puc:"+valor);
        if (valor == null) {
            System.out.println(fecha + " = puc:" + valor);
        }
        valoresHistoricos2rr.put(this.getKey(fecha), valor);
    }


    private HashMap<java.sql.Timestamp, double[][]> valoresHistoricos2rr;

}
